/**
 * Copyright (c) 2024-2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "ir_static.h"

#include "datatype.h"
#include "libabckit/include/c/ir_core.h"
#include "libabckit/include/c/statuses.h"
#include "libabckit/src/adapter_static/helpers_static.h"

#include "libabckit/include/c/metadata_core.h"
#include "libabckit/src/statuses_impl.h"
#include "libabckit/src/macros.h"
#include "libabckit/src/metadata_inspect_impl.h"
#include "libabckit/src/helpers_common.h"
#include "libabckit/src/ir_impl.h"
#include "libabckit/src/wrappers/pandasm_wrapper.h"

#include "static_core/assembler/assembly-program.h"
#include "static_core/assembler/mangling.h"

#include "static_core/compiler/optimizer/ir/graph.h"
#include "static_core/compiler/optimizer/ir/basicblock.h"
#include "static_core/compiler/optimizer/ir/inst.h"
#include "static_core/compiler/optimizer/analysis/loop_analyzer.h"

#include "generated/insn_info.h"

#include "abckit_intrinsics_opcodes.inc"

#include <cstdarg>
#include <cstdint>

namespace libabckit {

#include "inst_props_helpers_dynamic.inc"

// helpers for getting inst operand's info
// for dynamic insts helpers are autogenerated by inst_props_helpers_dynamic.inc.erb
static bool HasMethodIdOperandStatic(AbckitIsaApiStaticOpcode opcode)
{
    switch (opcode) {
        case ABCKIT_ISA_API_STATIC_OPCODE_INITOBJECT:
        case ABCKIT_ISA_API_STATIC_OPCODE_CALL_STATIC:
        case ABCKIT_ISA_API_STATIC_OPCODE_CALL_VIRTUAL:
            return true;
        default:
            return false;
    }
}

static int GetMethodIdOperandIndexStatic(AbckitIsaApiStaticOpcode opcode)
{
    switch (opcode) {
        case ABCKIT_ISA_API_STATIC_OPCODE_INITOBJECT:
        case ABCKIT_ISA_API_STATIC_OPCODE_CALL_STATIC:
        case ABCKIT_ISA_API_STATIC_OPCODE_CALL_VIRTUAL:
            return 0;
        default:
            return 0;
    }
}

static bool HasStringIdOperandStatic(AbckitIsaApiStaticOpcode opcode)
{
    switch (opcode) {
        case ABCKIT_ISA_API_STATIC_OPCODE_LOADSTRING:
            return true;
        default:
            return false;
    }
}

static int GetStringIdOperandIndexStatic(AbckitIsaApiStaticOpcode opcode)
{
    switch (opcode) {
        case ABCKIT_ISA_API_STATIC_OPCODE_LOADSTRING:
            return 0;
        default:
            return 0;
    }
}

static bool HasLiteralArrayIdOperandStatic(AbckitIsaApiStaticOpcode opcode)
{
    switch (opcode) {
        case ABCKIT_ISA_API_STATIC_OPCODE_LOADCONSTARRAY:
            return true;
        default:
            return false;
    }
}

static int GetLiteralArrayIdOperandIndexStatic(AbckitIsaApiStaticOpcode opcode)
{
    switch (opcode) {
        case ABCKIT_ISA_API_STATIC_OPCODE_LOADCONSTARRAY:
            return 0;
        default:
            return 0;
    }
}

[[maybe_unused]] static bool HasTypeIdOperandStatic(AbckitIsaApiStaticOpcode opcode)
{
    switch (opcode) {
        case ABCKIT_ISA_API_STATIC_OPCODE_NEWARRAY:
        case ABCKIT_ISA_API_STATIC_OPCODE_NEWOBJECT:
        case ABCKIT_ISA_API_STATIC_OPCODE_CHECKCAST:
        case ABCKIT_ISA_API_STATIC_OPCODE_ISINSTANCE:
            return true;
        default:
            return false;
    }
}

[[maybe_unused]] static int GetTypeIdOperandIndexStatic(AbckitIsaApiStaticOpcode opcode)
{
    switch (opcode) {
        case ABCKIT_ISA_API_STATIC_OPCODE_NEWARRAY:
        case ABCKIT_ISA_API_STATIC_OPCODE_NEWOBJECT:
        case ABCKIT_ISA_API_STATIC_OPCODE_CHECKCAST:
        case ABCKIT_ISA_API_STATIC_OPCODE_ISINSTANCE:
            return 0;
        default:
            return -1;
    }
}

// ========================================
// Api for Graph manipulation
// ========================================

AbckitInst *GfindOrCreateConstantI64Static(AbckitGraph *graph, int64_t value)
{
    LIBABCKIT_LOG_FUNC;
    if (graph == nullptr) {
        LIBABCKIT_LOG(DEBUG) << "nullptr argument\n";
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return nullptr;
    }
    auto constantI64Impl = graph->impl->FindOrCreateConstant(value);
    return CreateInstFromImpl(graph, constantI64Impl);
}

AbckitInst *GfindOrCreateConstantI32Static(AbckitGraph *graph, int32_t value)
{
    LIBABCKIT_LOG_FUNC;
    if (graph == nullptr) {
        LIBABCKIT_LOG(DEBUG) << "nullptr argument\n";
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return nullptr;
    }
    auto constantI32Impl = graph->impl->FindOrCreateConstant(value);
    return CreateInstFromImpl(graph, constantI32Impl);
}

AbckitInst *GfindOrCreateConstantU64Static(AbckitGraph *graph, uint64_t value)
{
    LIBABCKIT_LOG_FUNC;
    if (graph == nullptr) {
        LIBABCKIT_LOG(DEBUG) << "nullptr argument\n";
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return nullptr;
    }
    auto constantU64Impl = graph->impl->FindOrCreateConstant(value);
    return CreateInstFromImpl(graph, constantU64Impl);
}

AbckitInst *GfindOrCreateConstantF64Static(AbckitGraph *graph, double value)
{
    LIBABCKIT_LOG_FUNC;
    // check inputs are valid
    if (graph == nullptr) {
        LIBABCKIT_LOG(DEBUG) << "nullptr argument\n";
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return nullptr;
    }
    auto constantF64Impl = graph->impl->FindOrCreateConstant(value);
    return CreateInstFromImpl(graph, constantF64Impl);
}

uint32_t GgetNumberOfBasicBlocksStatic(AbckitGraph *graph)
{
    LIBABCKIT_LOG_FUNC;
    if (graph == nullptr) {
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return 0;
    }
    return graph->impl->GetVectorBlocks().size();
}

AbckitBasicBlock *GgetBasicBlockStatic(AbckitGraph *graph, uint32_t id)
{
    LIBABCKIT_LOG_FUNC;
    if (graph == nullptr) {
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return nullptr;
    }
    if (id >= graph->impl->GetVectorBlocks().size()) {
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return nullptr;
    }
    auto *bbImpl = graph->impl->GetVectorBlocks()[id];
    return graph->implToBB.at(bbImpl);
}

uint32_t GgetNumberOfParametersStatic(AbckitGraph *graph)
{
    auto list = graph->impl->GetParameters();
    auto ins = list.begin();
    uint32_t size = 0;
    while (ins != list.end()) {
        ++ins;
        ++size;
    }
    return size;
}

AbckitInst *GgetParameterStatic(AbckitGraph *graph, uint32_t index)
{
    LIBABCKIT_LOG_FUNC;
    if (graph == nullptr) {
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return nullptr;
    }

    if (index >= GgetNumberOfParametersStatic(graph)) {
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return nullptr;
    }

    auto list = graph->impl->GetParameters();
    auto ins = list.begin();
    for (uint32_t i = 0; i < index; i++) {
        if (ins != list.end()) {
            ++ins;
        }
    }
    return graph->implToInst.at(*ins);
}

void SetTryBlocks(AbckitGraph *graph, AbckitBasicBlock *tryLastBB, AbckitBasicBlock *tryBeginBB,
                  AbckitBasicBlock *tryEndBB)
{
    tryBeginBB->impl->SetTryBegin(true);
    tryBeginBB->impl->SetTryId(tryBeginBB->impl->GetId());
    graph->impl->AppendTryBeginBlock(tryBeginBB->impl);
    AbckitInst *tryInst = IcreateTryStatic(graph);
    BBaddInstFrontStatic(tryBeginBB, tryInst);

    tryEndBB->impl->SetTryEnd(true);
    tryEndBB->impl->SetTryId(tryBeginBB->impl->GetTryId());
    // NOTE(nsizov): NOTE, calculate guest pc for new created blocks (abckit-wise problem)
    tryEndBB->impl->SetGuestPc(tryLastBB->impl->GetGuestPc() - 1);
    tryInst->impl->CastToTry()->SetTryEndBlock(tryEndBB->impl);
    // NOTE(nsizov): NOTE, set proper typeid for catch blocks (abckit-wise problem)
    tryInst->impl->CastToTry()->AppendCatchTypeId(0, 1);
}

struct VisitorData {
    AbckitBasicBlock *cur;
    AbckitBasicBlock *tryBegin;
    AbckitBasicBlock *tryEnd;
    AbckitBasicBlock *catchEnd;
    std::unordered_set<AbckitBasicBlock *> *visited;
    std::queue<AbckitBasicBlock *> *toVisit;
};

void VisitBbs(AbckitBasicBlock *tryFirstBB, AbckitBasicBlock *tryLastBB, AbckitBasicBlock *catchBeginBB,
              AbckitBasicBlock *tryBeginBB, std::unordered_set<AbckitBasicBlock *> *visited,
              std::queue<AbckitBasicBlock *> *toVisit, VisitorData visitorData)
{
    visitorData.cur = tryFirstBB;

    // A data to pass into visitors traverse

    BBvisitPredBlocksStatic(tryFirstBB, &visitorData, [](AbckitBasicBlock *predBasicBlock, void *data) {
        AbckitBasicBlock *curBasicBlock = static_cast<struct VisitorData *>(data)->cur;
        AbckitBasicBlock *tryBeginBB = static_cast<struct VisitorData *>(data)->tryBegin;

        predBasicBlock->impl->ReplaceSucc(curBasicBlock->impl, tryBeginBB->impl);
        curBasicBlock->impl->RemovePred(predBasicBlock->impl);
        return true;
    });

    BBappendSuccBlockStatic(tryBeginBB, tryFirstBB);
    BBappendSuccBlockStatic(tryBeginBB, catchBeginBB);

    visitorData.cur = tryLastBB;

    BBvisitPredBlocksStatic(tryLastBB, &visitorData, [](AbckitBasicBlock *predBasicBlock, void *data) {
        AbckitBasicBlock *curBasicBlock = static_cast<struct VisitorData *>(data)->cur;
        AbckitBasicBlock *tryEndBB = static_cast<struct VisitorData *>(data)->tryEnd;

        predBasicBlock->impl->ReplaceSucc(curBasicBlock->impl, tryEndBB->impl);
        curBasicBlock->impl->RemovePred(predBasicBlock->impl);
        return true;
    });

    toVisit->push(tryBeginBB);

    while (!(*toVisit).empty()) {
        AbckitBasicBlock *curBB = toVisit->front();
        visitorData.cur = curBB;
        toVisit->pop();
        visited->insert(curBB);
        curBB->impl->SetTry(true);
        curBB->impl->SetTryId(tryBeginBB->impl->GetTryId());
        BBvisitSuccBlocksStatic(curBB, &visitorData, [](AbckitBasicBlock *succBasicBlock, void *data) {
            if (succBasicBlock->impl->IsTryEnd() || succBasicBlock->impl->IsCatchBegin() ||
                succBasicBlock->impl->IsEndBlock()) {
                return false;
            }

            auto *visited = static_cast<struct VisitorData *>(data)->visited;
            auto *toVisit = static_cast<struct VisitorData *>(data)->toVisit;

            auto it = visited->find(succBasicBlock);
            if (it == visited->end()) {
                toVisit->push(succBasicBlock);
            }
            return true;
        });
    }
}

bool CheckCatchEnv(AbckitBasicBlock *tryFirstBB, AbckitBasicBlock *tryLastBB)
{
    // NOTE(nsizov): check input for dominance tryFirstBB -> tryLastBB
    //               and catchBeginBB -> catchEndBB

    if (BBisStartStatic(tryFirstBB) || BBisEndStatic(tryFirstBB)) {
        LIBABCKIT_LOG(DEBUG) << "tryFirstBB cannot be niether 'start' nor 'end' block";
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return false;
    }

    if (BBisStartStatic(tryLastBB) || BBisEndStatic(tryLastBB)) {
        LIBABCKIT_LOG(DEBUG) << "tryLastBB cannot be niether 'start' nor 'end' block";
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return false;
    }
    return true;
}

void GinsertTryCatchStatic(AbckitBasicBlock *tryFirstBB, AbckitBasicBlock *tryLastBB, AbckitBasicBlock *catchBeginBB,
                           AbckitBasicBlock *catchEndBB)
{
    LIBABCKIT_LOG_FUNC;
    if (!CheckCatchEnv(tryFirstBB, tryLastBB)) {
        return;
    }

    AbckitGraph *graph = tryFirstBB->graph;

    // NOTE(nsizov): implement for static mode as well
    if (!IsDynamic(graph->function->owningModule->target)) {
        libabckit::statuses::SetLastError(ABCKIT_STATUS_WRONG_TARGET);
        return;
    }

    if ((graph != tryLastBB->graph) || (graph != catchBeginBB->graph) || (graph != catchEndBB->graph)) {
        SetLastError(ABCKIT_STATUS_WRONG_CTX);
        return;
    }

    AbckitBasicBlock *tryBeginBB = BBcreateEmptyStatic(graph);
    AbckitBasicBlock *tryEndBB = BBcreateEmptyStatic(graph);
    SetTryBlocks(graph, tryLastBB, tryBeginBB, tryEndBB);

    catchBeginBB->impl->SetCatchBegin(true);
    if (catchBeginBB != catchEndBB) {
        catchEndBB->impl->SetCatch(true);
    }

    std::unordered_set<AbckitBasicBlock *> visited;
    std::queue<AbckitBasicBlock *> toVisit;

    auto visitorData = VisitorData {nullptr, tryBeginBB, tryEndBB, catchEndBB, &visited, &toVisit};

    VisitBbs(tryFirstBB, tryLastBB, catchBeginBB, tryBeginBB, &visited, &toVisit, visitorData);

    visited.erase(visited.begin(), visited.end());
    std::queue<AbckitBasicBlock *>().swap(toVisit);

    while (!toVisit.empty()) {
        AbckitBasicBlock *curBB = toVisit.front();
        toVisit.pop();
        visited.insert(curBB);
        curBB->impl->SetCatch(true);
        curBB->impl->SetTryId(tryBeginBB->impl->GetTryId());
        BBvisitSuccBlocksStatic(curBB, &visitorData, [](AbckitBasicBlock *succBasicBlock, void *data) {
            auto catchEndBB = static_cast<struct VisitorData *>(data)->catchEnd;

            // NOTE(ivagin) if (succBasicBlock->impl->IsCatchEnd()
            if (succBasicBlock == catchEndBB || succBasicBlock->impl->IsTry()) {
                return false;
            }

            auto *visited = static_cast<struct VisitorData *>(data)->visited;
            auto *toVisit = static_cast<struct VisitorData *>(data)->toVisit;

            if (visited->find(succBasicBlock) == visited->end()) {
                toVisit->push(succBasicBlock);
            }
            return true;
        });
    }

    BBappendSuccBlockStatic(tryEndBB, tryLastBB);
    BBappendSuccBlockStatic(tryEndBB, catchBeginBB);
    BBappendSuccBlockStatic(catchEndBB, tryLastBB);
}

AbckitBasicBlock *GgetStartBasicBlockStatic(AbckitGraph *graph)
{
    LIBABCKIT_LOG_FUNC;
    LIBABCKIT_BAD_ARGUMENT(graph, nullptr);
    ark::compiler::BasicBlock *bbImpl = graph->impl->GetStartBlock();
    auto *bb = graph->implToBB.at(bbImpl);

    return bb;
}

AbckitBasicBlock *GgetEndBasicBlockStatic(AbckitGraph *graph)
{
    LIBABCKIT_LOG_FUNC;
    LIBABCKIT_BAD_ARGUMENT(graph, nullptr);
    ark::compiler::BasicBlock *bbImpl = graph->impl->GetEndBlock();
    auto *bb = graph->implToBB.at(bbImpl);

    return bb;
}

void IdumpStatic(AbckitInst *inst, int fd)
{
    LIBABCKIT_LOG_FUNC;
    std::stringstream ss;
    inst->impl->Dump(&ss);
    write(fd, ss.str().data(), ss.str().size());
}

void GdumpStatic(AbckitGraph *graph, int fd)
{
    LIBABCKIT_LOG_FUNC;
    if (GraphHasUnreachableBlocks(graph->impl)) {
        LIBABCKIT_LOG(DEBUG) << "Cannot dump, there are unreachable blocks in graph\n";
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return;
    }
    std::stringstream ss;
    graph->impl->Dump(&ss);
    write(fd, ss.str().data(), ss.str().size());
}

void GrunPassRemoveUnreachableBlocksStatic(AbckitGraph *graph)
{
    LIBABCKIT_LOG_FUNC;
    graph->impl->RemoveUnreachableBlocks();
    GraphInvalidateAnalyses(graph->impl);
}

bool GvisitBlocksRPOStatic(AbckitGraph *graph, void *data, bool (*cb)(AbckitBasicBlock *bb, void *data))
{
    LIBABCKIT_LOG_FUNC;
    LIBABCKIT_BAD_ARGUMENT(graph, false)
    LIBABCKIT_BAD_ARGUMENT(cb, false)

    if (GraphHasUnreachableBlocks(graph->impl)) {
        LIBABCKIT_LOG(DEBUG) << "Cannot get blocks RPO, there are unreachable blocks in graph\n";
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return false;
    }

    std::stringstream ss;
    for (auto *bbImpl : graph->impl->GetBlocksRPO()) {
        auto *bb = graph->implToBB.at(bbImpl);
        if (!cb(bb, data)) {
            return false;
        }
    }
    return true;
}

// ========================================
// Api for basic block manipulation
// ========================================

bool BBvisitSuccBlocksStatic(AbckitBasicBlock *curBasicBlock, void *data,
                             bool (*cb)(AbckitBasicBlock *succBasicBlock, void *data))
{
    LIBABCKIT_LOG_FUNC;
    LIBABCKIT_BAD_ARGUMENT(curBasicBlock, false)
    LIBABCKIT_BAD_ARGUMENT(cb, false)

    auto *graph = curBasicBlock->graph;
    auto *bbImpl = curBasicBlock->impl;
    auto succImpls = bbImpl->GetSuccsBlocks();
    for (auto *succImpl : succImpls) {
        auto *succ = graph->implToBB.at(succImpl);
        if (!cb(succ, data)) {
            return false;
        }
    }
    return true;
}

bool BBvisitPredBlocksStatic(AbckitBasicBlock *curBasicBlock, void *data,
                             bool (*cb)(AbckitBasicBlock *succBasicBlock, void *data))
{
    LIBABCKIT_LOG_FUNC;
    LIBABCKIT_BAD_ARGUMENT(curBasicBlock, false)
    LIBABCKIT_BAD_ARGUMENT(cb, false)

    auto *graph = curBasicBlock->graph;
    auto *bbImpl = curBasicBlock->impl;
    auto predImpls = bbImpl->GetPredsBlocks();
    for (auto *predImpl : predImpls) {
        auto *pred = graph->implToBB.at(predImpl);
        if (!cb(pred, data)) {
            return false;
        }
    }
    return true;
}

AbckitBasicBlock *BBcreateEmptyStatic(AbckitGraph *graph)
{
    LIBABCKIT_LOG_FUNC;
    LIBABCKIT_BAD_ARGUMENT(graph, nullptr);

    auto *bbImpl = graph->impl->CreateEmptyBlock(0U);
    auto *bb = graph->impl->GetLocalAllocator()->New<AbckitBasicBlock>();
    bb->graph = graph;
    bb->impl = bbImpl;
    graph->implToBB.insert({bbImpl, bb});
    GraphInvalidateAnalyses(graph->impl);
    return bb;
}

void BBaddInstFrontStatic(AbckitBasicBlock *basicBlock, AbckitInst *inst)
{
    LIBABCKIT_LOG_FUNC;
    LIBABCKIT_BAD_ARGUMENT_VOID(basicBlock)
    LIBABCKIT_BAD_ARGUMENT_VOID(inst)

    LIBABCKIT_WRONG_CTX_VOID(basicBlock->graph, inst->graph);

    if (inst->impl->IsConst()) {
        LIBABCKIT_LOG(DEBUG) << "can't use constant instruction as an argument";
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return;
    }

    basicBlock->impl->PrependInst(inst->impl);
}

AbckitBasicBlock *BBsplitBlockAfterInstructionStatic(AbckitBasicBlock *basicBlock, AbckitInst *inst, bool makeEdge)
{
    LIBABCKIT_BAD_ARGUMENT(basicBlock, nullptr);
    LIBABCKIT_BAD_ARGUMENT(inst, nullptr);

    auto *bbImpl = inst->impl->GetBasicBlock();
    auto *newBbImpl = bbImpl->SplitBlockAfterInstruction(inst->impl, makeEdge);

    auto *iBb = IgetBasicBlockStatic(inst);
    if (iBb != basicBlock) {
        LIBABCKIT_LOG(DEBUG) << "Instruction should be in basic block";
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return nullptr;
    }

    auto *bb = inst->graph->impl->GetLocalAllocator()->New<AbckitBasicBlock>();
    bb->graph = inst->graph;
    bb->impl = newBbImpl;
    inst->graph->implToBB.insert({newBbImpl, bb});
    GraphInvalidateAnalyses(basicBlock->graph->impl);
    return bb;
}

void BBaddInstBackStatic(AbckitBasicBlock *basicBlock, AbckitInst *inst)
{
    LIBABCKIT_LOG_FUNC;
    LIBABCKIT_BAD_ARGUMENT_VOID(basicBlock)
    LIBABCKIT_BAD_ARGUMENT_VOID(inst)

    LIBABCKIT_WRONG_CTX_VOID(basicBlock->graph, inst->graph);

    if (inst->impl->IsConst()) {
        LIBABCKIT_LOG(DEBUG) << "can't use constant instruction as an argument";
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return;
    }

    basicBlock->impl->AppendInst(inst->impl);
}

AbckitInst *BBgetLastInstStatic(AbckitBasicBlock *basicBlock)
{
    LIBABCKIT_LOG_FUNC;
    LIBABCKIT_BAD_ARGUMENT(basicBlock, nullptr);

    auto *bb = basicBlock->impl;
    auto *inst = bb->GetLastInst();
    if (inst == nullptr) {
        return nullptr;
    }
    return basicBlock->graph->implToInst.at(inst);
}

AbckitGraph *BBgetGraphStatic(AbckitBasicBlock *basicBlock)
{
    LIBABCKIT_LOG_FUNC;
    LIBABCKIT_BAD_ARGUMENT(basicBlock, nullptr);

    return basicBlock->graph;
}

AbckitInst *BBgetFirstInstStatic(AbckitBasicBlock *basicBlock)
{
    LIBABCKIT_LOG_FUNC;
    LIBABCKIT_BAD_ARGUMENT(basicBlock, nullptr);

    auto *bbImpl = basicBlock->impl;

    ark::compiler::Inst *instImpl = bbImpl->GetFirstPhi();
    if (instImpl == nullptr) {
        instImpl = bbImpl->GetFirstInst();
    }
    if (instImpl == nullptr) {
        return nullptr;
    }

    auto *inst = basicBlock->graph->implToInst.at(instImpl);
    return inst;
}

void BBdisconnectSuccBlockStatic(AbckitBasicBlock *bb, size_t index)
{
    LIBABCKIT_LOG_FUNC;
    LIBABCKIT_BAD_ARGUMENT_VOID(bb)

    if (index >= bb->impl->GetSuccsBlocks().size()) {
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return;
    }

    auto succ = bb->impl->GetSuccessor(index);
    succ->RemovePred(bb->impl);
    bb->impl->RemoveSucc(succ);
    GraphInvalidateAnalyses(bb->graph->impl);
}

void BBremoveAllInstsStatic(AbckitBasicBlock *basicBlock)
{
    LIBABCKIT_LOG_FUNC;
    basicBlock->impl->Clear();
}

AbckitBasicBlock *BBgetSuccBlockStatic(AbckitBasicBlock *basicBlock, uint32_t index)
{
    LIBABCKIT_LOG_FUNC;
    LIBABCKIT_BAD_ARGUMENT(basicBlock, nullptr);
    if (index >= basicBlock->impl->GetSuccsBlocks().size()) {
        statuses::SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return nullptr;
    }

    auto *succImpl = basicBlock->impl->GetSuccsBlocks()[index];

    return basicBlock->graph->implToBB.at(succImpl);
}

uint64_t BBgetSuccBlockCountStatic(AbckitBasicBlock *basicBlock)
{
    LIBABCKIT_LOG_FUNC;
    LIBABCKIT_BAD_ARGUMENT(basicBlock, 0);

    return basicBlock->impl->GetSuccsBlocks().size();
}

uint32_t BBgetIdStatic([[maybe_unused]] AbckitBasicBlock *basicBlock)
{
    LIBABCKIT_LOG_FUNC;
    LIBABCKIT_BAD_ARGUMENT(basicBlock, 0);

    return basicBlock->impl->GetId();
}

AbckitBasicBlock *BBgetPredBlockStatic(AbckitBasicBlock *basicBlock, uint32_t index)
{
    LIBABCKIT_LOG_FUNC;
    LIBABCKIT_BAD_ARGUMENT(basicBlock, nullptr);

    if (index >= basicBlock->impl->GetPredsBlocks().size()) {
        statuses::SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return nullptr;
    }

    auto *predImpl = basicBlock->impl->GetPredsBlocks()[index];

    return basicBlock->graph->implToBB.at(predImpl);
}

uint64_t BBgetPredBlockCountStatic(AbckitBasicBlock *basicBlock)
{
    LIBABCKIT_LOG_FUNC;
    LIBABCKIT_BAD_ARGUMENT(basicBlock, 0);

    return basicBlock->impl->GetPredsBlocks().size();
}

bool BBisStartStatic(AbckitBasicBlock *basicBlock)
{
    LIBABCKIT_LOG_FUNC;
    LIBABCKIT_BAD_ARGUMENT(basicBlock, false);

    return basicBlock->impl->IsStartBlock();
}

bool BBisEndStatic(AbckitBasicBlock *basicBlock)
{
    LIBABCKIT_LOG_FUNC;
    LIBABCKIT_BAD_ARGUMENT(basicBlock, false);

    return basicBlock->impl->IsEndBlock();
}

uint32_t BBgetNumberOfInstructionsStatic(AbckitBasicBlock *basicBlock)
{
    LIBABCKIT_LOG_FUNC;
    LIBABCKIT_BAD_ARGUMENT(basicBlock, 0);

    return basicBlock->impl->CountInsts();
}

bool BBisLoopHeadStatic(AbckitBasicBlock *basicBlock)
{
    LIBABCKIT_LOG_FUNC;
    LIBABCKIT_BAD_ARGUMENT(basicBlock, false);

    return basicBlock->impl->IsLoopHeader();
}

bool BBisLoopPreheadStatic(AbckitBasicBlock *basicBlock)
{
    LIBABCKIT_LOG_FUNC;
    LIBABCKIT_BAD_ARGUMENT(basicBlock, false);

    return basicBlock->impl->IsLoopPreHeader();
}

bool BBisTryBeginStatic(AbckitBasicBlock *basicBlock)
{
    LIBABCKIT_LOG_FUNC;
    LIBABCKIT_BAD_ARGUMENT(basicBlock, false);

    return basicBlock->impl->IsTryBegin();
}

bool BBisTryStatic(AbckitBasicBlock *basicBlock)
{
    LIBABCKIT_LOG_FUNC;
    LIBABCKIT_BAD_ARGUMENT(basicBlock, false);

    return basicBlock->impl->IsTry();
}

bool BBisTryEndStatic(AbckitBasicBlock *basicBlock)
{
    LIBABCKIT_LOG_FUNC;
    LIBABCKIT_BAD_ARGUMENT(basicBlock, false);

    return basicBlock->impl->IsTryEnd();
}

bool BBisCatchBeginStatic(AbckitBasicBlock *basicBlock)
{
    LIBABCKIT_LOG_FUNC;
    LIBABCKIT_BAD_ARGUMENT(basicBlock, false);

    return basicBlock->impl->IsCatchBegin();
}

bool BBisCatchStatic(AbckitBasicBlock *basicBlock)
{
    LIBABCKIT_LOG_FUNC;
    LIBABCKIT_BAD_ARGUMENT(basicBlock, false);

    return basicBlock->impl->IsCatch();
}

void BBdumpStatic(AbckitBasicBlock *basicBlock, int fd)
{
    LIBABCKIT_LOG_FUNC;
    LIBABCKIT_BAD_ARGUMENT_VOID(basicBlock)

    std::stringstream ss;
    basicBlock->impl->Dump(&ss);
    write(fd, ss.str().data(), ss.str().size());
}

bool BBcheckDominanceStatic(AbckitBasicBlock *basicBlock, AbckitBasicBlock *dominator)
{
    LIBABCKIT_LOG_FUNC;
    LIBABCKIT_BAD_ARGUMENT(basicBlock, false);
    LIBABCKIT_BAD_ARGUMENT(dominator, false);

    LIBABCKIT_WRONG_CTX(basicBlock->graph, dominator->graph, false);

    if (!GraphDominatorsTreeAnalysisIsValid(basicBlock->graph->impl)) {
        LIBABCKIT_LOG(DEBUG) << "DominatorsTree analysis is not valid\n";
        statuses::SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return false;
    }

    return dominator->impl->IsDominate(basicBlock->impl);
}

AbckitBasicBlock *BBgetImmediateDominatorStatic(AbckitBasicBlock *basicBlock)
{
    LIBABCKIT_LOG_FUNC;
    LIBABCKIT_BAD_ARGUMENT(basicBlock, nullptr);

    if (!GraphDominatorsTreeAnalysisIsValid(basicBlock->graph->impl)) {
        LIBABCKIT_LOG(DEBUG) << "DominatorsTree analysis is not valid\n";
        statuses::SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return nullptr;
    }

    auto *bb = basicBlock->impl->GetDominator();
    if (bb == nullptr) {
        return nullptr;
    }
    return basicBlock->graph->implToBB.at(bb);
}

bool BBvisitDominatedBlocksStatic(AbckitBasicBlock *basicBlock, void *data,
                                  bool (*cb)(AbckitBasicBlock *dominatedBasicBlock, void *data))
{
    LIBABCKIT_LOG_FUNC;
    LIBABCKIT_BAD_ARGUMENT(basicBlock, false)
    LIBABCKIT_BAD_ARGUMENT(cb, false)

    if (!GraphDominatorsTreeAnalysisIsValid(basicBlock->graph->impl)) {
        LIBABCKIT_LOG(DEBUG) << "DominatorsTree analysis is not valid\n";
        statuses::SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return false;
    }

    for (auto *bbImpl : basicBlock->impl->GetDominatedBlocks()) {
        auto *bb = basicBlock->graph->implToBB.at(bbImpl);
        if (!cb(bb, data)) {
            return false;
        }
    }
    return true;
}

void BBinsertSuccBlockStatic(AbckitBasicBlock *basicBlock, AbckitBasicBlock *succBlock, uint32_t index)
{
    LIBABCKIT_LOG_FUNC;
    LIBABCKIT_BAD_ARGUMENT_VOID(basicBlock)
    LIBABCKIT_BAD_ARGUMENT_VOID(succBlock)

    LIBABCKIT_WRONG_CTX_VOID(basicBlock->graph, succBlock->graph);

    auto &bbs = basicBlock->impl->GetSuccsBlocks();
    if (index > bbs.size()) {
        statuses::SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return;
    }

    if (index == bbs.size()) {
        bbs.emplace_back(succBlock->impl);
    } else {
        bbs.resize(bbs.size() + 1);
        for (size_t i = index + 1; i < bbs.size(); i++) {
            bbs[i] = bbs[i - 1];
        }
        bbs[index] = succBlock->impl;
    }

    succBlock->impl->GetPredsBlocks().emplace_back(basicBlock->impl);
    GraphInvalidateAnalyses(basicBlock->graph->impl);
}

void BBappendSuccBlockStatic(AbckitBasicBlock *basicBlock, AbckitBasicBlock *succBlock)
{
    LIBABCKIT_LOG_FUNC;
    LIBABCKIT_BAD_ARGUMENT_VOID(basicBlock)
    LIBABCKIT_BAD_ARGUMENT_VOID(succBlock)

    LIBABCKIT_WRONG_CTX_VOID(basicBlock->graph, succBlock->graph);

    basicBlock->impl->GetSuccsBlocks().emplace_back(succBlock->impl);
    succBlock->impl->GetPredsBlocks().emplace_back(basicBlock->impl);
    GraphInvalidateAnalyses(basicBlock->graph->impl);
}

AbckitBasicBlock *BBgetTrueBranchStatic(AbckitBasicBlock *bb)
{
    LIBABCKIT_LOG_FUNC;
    LIBABCKIT_BAD_ARGUMENT(bb, nullptr);

    if (bb->impl->GetSuccsBlocks().empty()) {
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return nullptr;
    }
    auto *tb = bb->impl->GetTrueSuccessor();
    ASSERT(tb != nullptr);

    return bb->graph->implToBB.at(tb);
}

AbckitBasicBlock *BBgetFalseBranchStatic(AbckitBasicBlock *bb)
{
    LIBABCKIT_LOG_FUNC;
    LIBABCKIT_BAD_ARGUMENT(bb, nullptr);

    if (bb->impl->GetSuccsBlocks().size() < 2U) {
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return nullptr;
    }
    auto *fb = bb->impl->GetFalseSuccessor();
    ASSERT(fb != nullptr);

    return bb->graph->implToBB.at(fb);
}

AbckitInst *BBcreatePhiStatic(AbckitBasicBlock *bb, size_t argCount, std::va_list args)
{
    LIBABCKIT_LOG_FUNC;

    LIBABCKIT_LOG(DEBUG) << argCount << '\n';
    if (argCount < 1) {
        LIBABCKIT_LOG(DEBUG) << "not enough inputs\n";
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return nullptr;
    }

    std::vector<AbckitInst *> inputs;

    for (size_t index = 0; index < argCount; ++index) {
        // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
        AbckitInst *input = va_arg(args, AbckitInst *);
        LIBABCKIT_BAD_ARGUMENT(input, nullptr);
        inputs.emplace_back(input);
    }

    ark::compiler::DataType::Type type = inputs[0]->impl->GetType();
    if (IsDynamic(bb->graph->function->owningModule->target)) {
        type = ark::compiler::DataType::ANY;
    }

    for (auto *inst : inputs) {
        if (IsDynamic(bb->graph->function->owningModule->target)) {
            if (inst->impl->GetType() != ark::compiler::DataType::INT64 &&
                inst->impl->GetType() != ark::compiler::DataType::ANY) {
                LIBABCKIT_LOG(DEBUG) << "inconsistent input types\n";
                SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
                return nullptr;
            }
        } else {
            if (type != inst->impl->GetType()) {
                LIBABCKIT_LOG(DEBUG) << "inconsistent input types\n";
                SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
                return nullptr;
            }
        }
    }

    auto phiImpl = bb->graph->impl->CreateInstPhi(type, 0);
    bb->impl->AppendPhi(phiImpl);
    auto *phi = CreateInstFromImpl(bb->graph, phiImpl);
    for (auto *inst : inputs) {
        phiImpl->AppendInput(inst->impl);
    }
    return phi;
}

AbckitInst *BBcreateCatchPhiStatic(AbckitBasicBlock *catchBegin, size_t argCount, std::va_list args)
{
    auto *instImpl = catchBegin->graph->impl->CreateInstCatchPhi();
    auto *catchPhi = CreateInstFromImpl(catchBegin->graph, instImpl);

    BBaddInstFrontStatic(catchBegin, catchPhi);

    if (argCount == 0) {
        auto type = IsDynamic(catchBegin->graph->function->owningModule->target) ? ark::compiler::DataType::ANY
                                                                                 : ark::compiler::DataType::REFERENCE;
        instImpl->SetIsAcc();
        instImpl->SetType(type);
        return catchPhi;
    }

    std::vector<ark::compiler::DataType::Type> types;

    for (size_t index = 0; index < argCount; ++index) {
        // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
        AbckitInst *inputOrThrowable = va_arg(args, AbckitInst *);
        if (index % 2U == 0) {
            types.push_back(inputOrThrowable->impl->GetType());
            catchPhi->impl->AppendInput(inputOrThrowable->impl);
        } else {
            catchPhi->impl->CastToCatchPhi()->AppendThrowableInst(inputOrThrowable->impl);
        }
    }

    ASSERT(!types.empty());

    if (IsDynamic(catchBegin->graph->function->owningModule->target)) {
        catchPhi->impl->SetType(ark::compiler::DataType::ANY);
    } else {
        for (int i = 1, j = types.size(); i < j; ++i) {
            if (types[0] != types[i]) {
                LIBABCKIT_LOG(DEBUG) << "All inputs of a catchPhi should be of the same type " << '\n';
                SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
                catchBegin->impl->EraseInst(instImpl, true);
                return nullptr;
            }
        }
        catchPhi->impl->SetType(types[0]);
    }

    return catchPhi;
}

// ========================================
// Api for instruction manipulation
// ========================================

void IremoveStatic(AbckitInst *inst)
{
    LIBABCKIT_LOG_FUNC;

    auto bbImpl = inst->impl->GetBasicBlock();
    LIBABCKIT_BAD_ARGUMENT_VOID(bbImpl)

    bbImpl->RemoveInst(inst->impl);
}

uint32_t IgetIdStatic(AbckitInst *inst)
{
    LIBABCKIT_LOG_FUNC;
    auto id = inst->impl->GetId();
    LIBABCKIT_LOG(DEBUG) << id << '\n';
    return id;
}

AbckitLiteralArray *IgetLiteralArrayStatic(AbckitInst *inst)
{
    LIBABCKIT_LOG_FUNC;
    size_t idx = 0;
    if (IsDynamic(inst->graph->function->owningModule->target)) {
        auto instOpcode = GetDynamicOpcode(inst->impl);
        if (!HasLiteralArrayIdOperandDynamic(instOpcode)) {
            statuses::SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
            return nullptr;
        }
        idx = GetLiteralArrayIdOperandIndexDynamic(instOpcode);
    } else {
        auto instOpcode = GetStaticOpcode(inst->impl);
        if (!HasLiteralArrayIdOperandStatic(instOpcode)) {
            statuses::SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
            return nullptr;
        }
        idx = GetLiteralArrayIdOperandIndexStatic(instOpcode);
    }
    auto &imms = inst->impl->CastToIntrinsic()->GetImms();
    ASSERT(!imms.empty());
    auto arrName = inst->graph->irInterface->literalarrays.at(imms[idx]);
    std::variant<ark::pandasm::LiteralArray *, panda::pandasm::LiteralArray *> arrImpl {
        (panda::pandasm::LiteralArray *)nullptr};
    if (IsDynamic(inst->graph->function->owningModule->target)) {
        arrImpl = reinterpret_cast<panda::pandasm::LiteralArray *>(
            PandasmWrapper::GetUnwrappedLiteralArrayTable(inst->graph->file->GetDynamicProgram()).at(arrName));
    } else {
        arrImpl = &inst->graph->file->GetStaticProgram()->literalarrayTable.at(arrName);
    }

    // Search through already created litarrs
    auto &abckitLitArrs = inst->graph->file->litarrs;
    for (auto &item : abckitLitArrs) {
        if (item->impl == arrImpl) {
            return item.get();
        }
    }

    // Create new litarr
    auto litarr = std::make_unique<AbckitLiteralArray>();
    litarr->file = inst->graph->file;
    litarr->impl = arrImpl;
    return abckitLitArrs.emplace_back(std::move(litarr)).get();
}

void IsetLiteralArrayStatic(AbckitInst *inst, AbckitLiteralArray *la)
{
    LIBABCKIT_LOG_FUNC;
    size_t idx = 0;
    if (IsDynamic(inst->graph->function->owningModule->target)) {
        auto instOpcode = GetDynamicOpcode(inst->impl);
        if (!HasLiteralArrayIdOperandDynamic(instOpcode)) {
            statuses::SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
            return;
        }
        idx = GetLiteralArrayIdOperandIndexDynamic(instOpcode);
    } else {
        auto instOpcode = GetStaticOpcode(inst->impl);
        if (!HasLiteralArrayIdOperandStatic(instOpcode)) {
            statuses::SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
            return;
        }
        idx = GetLiteralArrayIdOperandIndexStatic(instOpcode);
    }

    auto &imms = inst->impl->CastToIntrinsic()->GetImms();
    ASSERT(imms.size() == 2U);
    imms[idx] = GetLiteralArrayOffset(inst->graph, la);
}

AbckitString *IgetStringStatic(AbckitInst *inst)
{
    LIBABCKIT_LOG_FUNC;
    size_t idx = 0;
    if (IsDynamic(inst->graph->function->owningModule->target)) {
        auto instOpcode = GetDynamicOpcode(inst->impl);
        if (!HasStringIdOperandDynamic(instOpcode)) {
            statuses::SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
            return nullptr;
        }
        idx = GetStringIdOperandIndexDynamic(instOpcode);
    } else {
        auto instOpcode = GetStaticOpcode(inst->impl);
        if (!HasStringIdOperandStatic(instOpcode)) {
            statuses::SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
            return nullptr;
        }
        idx = GetStringIdOperandIndexStatic(instOpcode);
    }

    auto &imms = inst->impl->CastToIntrinsic()->GetImms();
    auto strName = inst->graph->irInterface->strings.at(imms[idx]);
    auto strImpl = inst->graph->file->strings.at(strName).get();
    return strImpl;
}

void IsetStringStatic(AbckitInst *inst, AbckitString *str)
{
    LIBABCKIT_LOG_FUNC;
    size_t idx = 0;
    if (IsDynamic(inst->graph->function->owningModule->target)) {
        auto instOpcode = GetDynamicOpcode(inst->impl);
        if (!HasStringIdOperandDynamic(instOpcode)) {
            statuses::SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
            return;
        }
        idx = GetStringIdOperandIndexDynamic(instOpcode);
    } else {
        auto instOpcode = GetStaticOpcode(inst->impl);
        if (!HasStringIdOperandStatic(instOpcode)) {
            statuses::SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
            return;
        }
        idx = GetStringIdOperandIndexStatic(instOpcode);
    }

    auto &imms = inst->impl->CastToIntrinsic()->GetImms();
    imms[idx] = GetStringOffset(inst->graph, str);
}

AbckitIsaApiStaticOpcode IgetOpcodeStaticStatic(AbckitInst *inst)
{
    LIBABCKIT_LOG_FUNC;
    auto opc = inst->impl->GetOpcode();
    if (opc == ark::compiler::Opcode::Intrinsic) {
        return GetStaticIntrinsicOpcode(inst->impl->CastToIntrinsic());
    }
    return GetStaticOpcode(inst->impl);
}

AbckitIsaApiDynamicOpcode IgetOpcodeDynamicStatic(AbckitInst *inst)
{
    LIBABCKIT_LOG_FUNC;
    auto opc = inst->impl->GetOpcode();
    if (opc == ark::compiler::Opcode::Intrinsic) {
        switch (inst->impl->CastToIntrinsic()->GetIntrinsicId()) {
            case ark::compiler::RuntimeInterface::IntrinsicId::INTRINSIC_ABCKIT_LOAD_STRING:
                return ABCKIT_ISA_API_DYNAMIC_OPCODE_LOADSTRING;
            default:
                break;
        }
        return GetDynamicIntrinsicOpcode(inst->impl->CastToIntrinsic());
    }
    return GetDynamicOpcode(inst->impl);
}

AbckitInst *IgetNextStatic(AbckitInst *inst)
{
    LIBABCKIT_LOG_FUNC;

    auto *nextInstImpl = inst->impl->GetNext();
    if (nextInstImpl == nullptr) {
        return nullptr;
    }
    auto *nextInst = inst->graph->implToInst.at(nextInstImpl);
    return nextInst;
}

AbckitInst *IgetPrevStatic(AbckitInst *inst)
{
    LIBABCKIT_LOG_FUNC;
    auto *nextInstImpl = inst->impl->GetPrev();
    if (nextInstImpl == nullptr) {
        return nullptr;
    }
    auto *nextInst = inst->graph->implToInst.at(nextInstImpl);
    return nextInst;
}

void IinsertAfterStatic(AbckitInst *inst, AbckitInst *next)
{
    LIBABCKIT_LOG_FUNC;
    LIBABCKIT_BAD_ARGUMENT_VOID(inst)
    LIBABCKIT_BAD_ARGUMENT_VOID(next)

    LIBABCKIT_WRONG_CTX_VOID(inst->graph, next->graph);

    auto *bb = IgetBasicBlockStatic(next);
    LIBABCKIT_BAD_ARGUMENT_VOID(bb)

    if (inst->impl->IsConst() || next->impl->IsConst()) {
        LIBABCKIT_LOG(DEBUG) << "can't use constant instruction as an argument";
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return;
    }

    bb->impl->InsertAfter(inst->impl, next->impl);
}

void IinsertBeforeStatic(AbckitInst *inst, AbckitInst *prev)
{
    LIBABCKIT_LOG_FUNC;
    LIBABCKIT_BAD_ARGUMENT_VOID(inst)
    LIBABCKIT_BAD_ARGUMENT_VOID(prev)

    LIBABCKIT_WRONG_CTX_VOID(inst->graph, prev->graph);

    auto *bb = IgetBasicBlockStatic(prev);
    LIBABCKIT_BAD_ARGUMENT_VOID(bb)

    if (inst->impl->IsConst() || prev->impl->IsConst()) {
        LIBABCKIT_LOG(DEBUG) << "can't use constant instruction as an argument";
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return;
    }
    bb->impl->InsertBefore(inst->impl, prev->impl);
}

bool IcheckDominanceStatic(AbckitInst *inst, AbckitInst *dominator)
{
    LIBABCKIT_LOG_FUNC;

    LIBABCKIT_WRONG_CTX(inst->graph, dominator->graph, false);

    return inst->impl->IsDominate(dominator->impl);
}

bool IvisitUsersStatic(AbckitInst *inst, void *data, bool (*cb)(AbckitInst *user, void *data))
{
    LIBABCKIT_LOG_FUNC;

    auto *user = inst->impl->GetFirstUser();

    while (user != nullptr) {
        auto *userInst = inst->graph->implToInst.at(user->GetInst());
        if (!cb(userInst, data)) {
            return false;
        }
        user = user->GetNext();
    }
    return true;
}

uint32_t IgetUserCountStatic(AbckitInst *inst)
{
    LIBABCKIT_LOG_FUNC;
    uint32_t count = 0;

    auto *user = inst->impl->GetFirstUser();

    while (user != nullptr) {
        count++;
        user = user->GetNext();
    }
    return count;
}

bool IvisitInputsStatic(AbckitInst *inst, void *data, bool (*cb)(AbckitInst *input, size_t inputIdx, void *data))
{
    LIBABCKIT_LOG_FUNC;
    for (size_t i = 0; i < inst->impl->GetInputsCount(); i++) {
        auto *inputImpl = inst->impl->GetInput(i).GetInst();
        auto *input = inst->graph->implToInst.at(inputImpl);
        if (!cb(input, i, data)) {
            return false;
        }
    }
    return true;
}

uint64_t IgetInputCountStatic(AbckitInst *inst)
{
    LIBABCKIT_LOG_FUNC;

    return inst->impl->GetInputsCount();
}

AbckitInst *IgetInputStatic(AbckitInst *inst, size_t index)
{
    LIBABCKIT_LOG_FUNC;
    if (inst->impl->GetInputsCount() <= index) {
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return nullptr;
    }

    auto *inputImpl = inst->impl->GetInput(index).GetInst();
    auto *input = inst->graph->implToInst.at(inputImpl);
    return input;
}

void IsetInputStatic(AbckitInst *inst, AbckitInst *input, int32_t index)
{
    LIBABCKIT_LOG_FUNC;
    LIBABCKIT_LOG(DEBUG) << index << '\n';
    LIBABCKIT_LOG(DEBUG) << inst->impl->GetInputs().size() << '\n';
    inst->impl->SetInput(index, input->impl);
}

void IsetInputsStatic(AbckitInst *inst, size_t argCount, std::va_list args)
{
    LIBABCKIT_LOG_FUNC;
    LIBABCKIT_LOG(DEBUG) << argCount << '\n';
    LIBABCKIT_LOG(DEBUG) << inst->impl->GetInputs().size() << '\n';
    for (size_t index = 0; index < argCount; ++index) {
        // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
        AbckitInst *input = va_arg(args, AbckitInst *);
        inst->impl->SetInput(index, input->impl);
    }
}

void IappendInputStatic(AbckitInst *inst, AbckitInst *input)
{
    LIBABCKIT_LOG_FUNC;
    auto instImpl = inst->impl;
    static size_t rangeInputsCount = -1;

    if (instImpl->IsOperandsDynamic()) {
        switch (instImpl->GetOpcode()) {
            case ark::compiler::Opcode::CallStatic: {
                instImpl->CastToCallStatic()->AppendInput(input->impl, input->impl->GetType());
                return;
            }
            case ark::compiler::Opcode::CallVirtual: {
                instImpl->CastToCallVirtual()->AppendInput(input->impl, input->impl->GetType());
                return;
            }
            case ark::compiler::Opcode::Intrinsic: {
                if (GetIntrinicMaxInputsCount(inst) == rangeInputsCount) {
                    instImpl->CastToIntrinsic()->AppendInput(input->impl, input->impl->GetType());
                    return;
                }
                if (instImpl->GetInputsCount() >= GetIntrinicMaxInputsCount(inst)) {
                    LIBABCKIT_LOG(DEBUG) << "The maximum number of inputs has been reached for the instruction\n";
                    SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
                    return;
                }
                instImpl->CastToIntrinsic()->AppendInput(input->impl, input->impl->GetType());
                return;
            }
            case ark::compiler::Opcode::Phi: {
                instImpl->AppendInput(input->impl);
                return;
            }
            default:
                break;
        }
    }

    LIBABCKIT_LOG(DEBUG) << "The instruction does not have the ability to add inputs\n";
    SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
}

// Type helpers

static AbckitType *CreateGeneralType(AbckitFile *file, AbckitTypeId typeId, AbckitCoreClass *klass)
{
    return GetOrCreateType(file, typeId, 0, klass);
}

AbckitType *IgetTypeStatic(AbckitInst *inst)
{
    LIBABCKIT_LOG_FUNC;
    AbckitTypeId typeId = TypeToTypeId(inst->impl->GetType());
    AbckitCoreClass *klass = nullptr;
    if (typeId != ABCKIT_TYPE_ID_REFERENCE) {
        return CreateGeneralType(inst->graph->file, typeId, klass);
    }
    // Add get of ABCKIT_TYPE_ID_REFERENCE NOTE(ymolokanov)
    return CreateGeneralType(inst->graph->file, typeId, klass);
}

AbckitTypeId IgetTargetTypeStatic(AbckitInst *inst)
{
    LIBABCKIT_LOG_FUNC;
    if (inst->impl->GetOpcode() != ark::compiler::Opcode::Cast) {
        LIBABCKIT_LOG(DEBUG) << "Instruction is not a cast\n";
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return AbckitTypeId::ABCKIT_TYPE_ID_INVALID;
    }

    return TypeToTypeId(static_cast<ark::compiler::CastInst *>(inst->impl)->GetType());
}

void IsetTargetTypeStatic(AbckitInst *inst, AbckitTypeId type)
{
    LIBABCKIT_LOG_FUNC;
    if (inst->impl->GetOpcode() != ark::compiler::Opcode::Cast) {
        LIBABCKIT_LOG(DEBUG) << "Instruction is not a cast\n";
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return;
    }

    inst->impl->SetType(TypeIdToType(type));
}

bool IcheckIsCallStatic(AbckitInst *inst)
{
    LIBABCKIT_LOG_FUNC;
    return IsCallInst(inst);
}

AbckitBasicBlock *IgetBasicBlockStatic(AbckitInst *inst)
{
    LIBABCKIT_LOG_FUNC;
    auto *graph = inst->graph;
    auto *implBB = inst->impl->GetBasicBlock();
    auto it = graph->implToBB.find(implBB);
    if (it != graph->implToBB.end()) {
        return it->second;
    }
    return nullptr;
}

AbckitGraph *IgetGraphStatic(AbckitInst *inst)
{
    LIBABCKIT_LOG_FUNC;
    return inst->graph;
}

AbckitCoreFunction *IgetFunctionStatic(AbckitInst *inst)
{
    LIBABCKIT_LOG_FUNC;
    auto *graph = inst->graph;

    ark::compiler::RuntimeInterface::MethodPtr methodPtr = nullptr;
    if (inst->impl->IsCall()) {
        auto *callInst = static_cast<ark::compiler::CallInst *>(inst->impl);
        methodPtr = callInst->GetCallMethod();
    } else if (inst->impl->IsIntrinsic()) {
        size_t idx = 0;
        if (IsDynamic(inst->graph->function->owningModule->target)) {
            auto instOpcode = GetDynamicOpcode(inst->impl);
            if (!HasMethodIdOperandDynamic(instOpcode)) {
                statuses::SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
                return nullptr;
            }
            idx = GetMethodIdOperandIndexDynamic(instOpcode);
        } else {
            auto instOpcode = GetStaticOpcode(inst->impl);
            if (!HasMethodIdOperandStatic(instOpcode)) {
                statuses::SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
                return nullptr;
            }
            idx = GetMethodIdOperandIndexStatic(instOpcode);
        }

        auto *intrinsic = inst->impl->CastToIntrinsic();
        methodPtr = reinterpret_cast<ark::compiler::RuntimeInterface::MethodPtr>(intrinsic->GetImm(idx));
    } else {
        LIBABCKIT_LOG(DEBUG) << "Instruction is not a call or intrinsic\n";
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return nullptr;
    }

    auto it = graph->irInterface->methods.find(reinterpret_cast<uintptr_t>(methodPtr));
    if (it == graph->irInterface->methods.end()) {
        LIBABCKIT_LOG(DEBUG) << "No requested call exists in current graph context\n";
        SetLastError(ABCKIT_STATUS_UNSUPPORTED);
        return nullptr;
    }

    auto &nameToFunction = reinterpret_cast<CtxGInternal *>(graph->internal)->runtimeAdapter->IsMethodStatic(methodPtr)
                               ? graph->file->nameToFunctionStatic
                               : graph->file->nameToFunctionInstance;
    if (nameToFunction.count(it->second) == 0) {
        statuses::SetLastError(ABCKIT_STATUS_UNSUPPORTED);
        return nullptr;
    }
    return nameToFunction[it->second];
}

void IsetFunctionStatic(AbckitInst *inst, AbckitCoreFunction *function)
{
    LIBABCKIT_LOG_FUNC;
    auto *graph = inst->graph;

    auto methodOffset = GetMethodOffset(graph, function);

    if (inst->impl->IsCall()) {
        auto *callInst = static_cast<ark::compiler::CallInst *>(inst->impl);
        callInst->SetCallMethodId(methodOffset);
    } else if (inst->impl->IsIntrinsic()) {
        size_t idx = 0;
        if (IsDynamic(inst->graph->function->owningModule->target)) {
            auto instOpcode = GetDynamicOpcode(inst->impl);
            if (!HasMethodIdOperandDynamic(instOpcode)) {
                statuses::SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
                return;
            }
            idx = GetMethodIdOperandIndexDynamic(instOpcode);
        } else {
            auto instOpcode = GetStaticOpcode(inst->impl);
            if (!HasMethodIdOperandStatic(instOpcode)) {
                statuses::SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
                return;
            }
            idx = GetMethodIdOperandIndexStatic(instOpcode);
        }

        auto *intrinsic = inst->impl->CastToIntrinsic();
        intrinsic->SetImm(idx, methodOffset);
    } else {
        LIBABCKIT_LOG(DEBUG) << "Instruction is not a call or intrinsic\n";
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return;
    }
}

void IsetClassStatic(AbckitInst *inst, AbckitCoreClass *klass)
{
    LIBABCKIT_LOG_FUNC;
    auto *graph = inst->graph;
    auto *intrinsic = inst->impl->CastToIntrinsic();
    auto instOpcode = GetStaticOpcode(inst->impl);
    if (!HasTypeIdOperandStatic(instOpcode)) {
        statuses::SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return;
    }
    size_t idx = GetTypeIdOperandIndexStatic(instOpcode);
    auto klassOffset = GetClassOffset(graph, klass);
    intrinsic->SetImm(idx, klassOffset);
}

AbckitCoreClass *IgetClassStatic(AbckitInst *inst)
{
    LIBABCKIT_LOG_FUNC;
    auto *graph = inst->graph;
    auto *intrinsic = inst->impl->CastToIntrinsic();
    auto instOpcode = IgetOpcodeStaticStatic(inst);
    if (!HasTypeIdOperandStatic(instOpcode)) {
        statuses::SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return nullptr;
    }
    size_t idx = GetTypeIdOperandIndexStatic(instOpcode);
    auto classPtr = reinterpret_cast<ark::compiler::RuntimeInterface::ClassPtr>(intrinsic->GetImm(idx));

    auto it = graph->ptrToClass.find(reinterpret_cast<uintptr_t>(classPtr));
    if (it == graph->ptrToClass.end()) {
        LIBABCKIT_LOG(DEBUG) << "No requested class exists in current graph context\n";
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return nullptr;
    }
    return it->second;
}

AbckitInst *GcreateNullPtrStatic(AbckitGraph *graph)
{
    auto instImpl = graph->impl->CreateInstNullPtr(ark::compiler::DataType::REFERENCE);
    auto *inst = CreateInstFromImpl(graph, instImpl);
    graph->impl->GetStartBlock()->AppendInst(instImpl);
    return inst;
}

int32_t IgetConstantValueI32Static(AbckitInst *inst)
{
    if (!inst->impl->IsConst()) {
        LIBABCKIT_LOG(DEBUG) << "Input instruction of " << LIBABCKIT_FUNC_NAME << " must be constant instruction"
                             << '\n';
    }
    if (inst->impl->GetType() != ark::compiler::DataType::INT32 &&
        inst->impl->GetType() != ark::compiler::DataType::UINT32) {
        LIBABCKIT_LOG(DEBUG) << "Type of input constant instruction in " << LIBABCKIT_FUNC_NAME << " is wrong" << '\n';
    }
    return static_cast<ark::compiler::ConstantInst *>(inst->impl)->GetInt32Value();
}

int64_t IgetConstantValueI64Static(AbckitInst *inst)
{
    if (!inst->impl->IsConst()) {
        LIBABCKIT_LOG(DEBUG) << "Input instruction of " << LIBABCKIT_FUNC_NAME << " must be constant instruction"
                             << '\n';
    }
    if (inst->impl->GetType() != ark::compiler::DataType::INT64 &&
        inst->impl->GetType() != ark::compiler::DataType::UINT64) {
        LIBABCKIT_LOG(DEBUG) << "Type of input constant instruction in " << LIBABCKIT_FUNC_NAME << " is wrong" << '\n';
    }
    return static_cast<ark::compiler::ConstantInst *>(inst->impl)->GetInt64Value();
}

uint64_t IgetConstantValueU64Static(AbckitInst *inst)
{
    if (!inst->impl->IsConst()) {
        LIBABCKIT_LOG(DEBUG) << "Input instruction of " << LIBABCKIT_FUNC_NAME << " must be constant instruction"
                             << '\n';
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
    }
    if (inst->impl->GetType() != ark::compiler::DataType::INT64 &&
        inst->impl->GetType() != ark::compiler::DataType::UINT64) {
        LIBABCKIT_LOG(DEBUG) << "Type of input constant instruction in " << LIBABCKIT_FUNC_NAME << " is wrong" << '\n';
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
    }
    return static_cast<ark::compiler::ConstantInst *>(inst->impl)->GetInt64Value();
}

double IgetConstantValueF64Static(AbckitInst *inst)
{
    if (!inst->impl->IsConst()) {
        LIBABCKIT_LOG(DEBUG) << "Input instruction of " << LIBABCKIT_FUNC_NAME << " must be constant instruction"
                             << '\n';
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
    }
    if (inst->impl->GetType() != ark::compiler::DataType::FLOAT64) {
        LIBABCKIT_LOG(DEBUG) << "Type of input constant instruction in " << LIBABCKIT_FUNC_NAME << " is wrong" << '\n';
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
    }
    return static_cast<ark::compiler::ConstantInst *>(inst->impl)->GetDoubleValue();
}

uint64_t IgetImmediateStatic(AbckitInst *inst, size_t idx)
{
    LIBABCKIT_LOG_FUNC;

    if (IgetImmediateCountStatic(inst) <= idx) {
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return 0;
    }

    uint64_t ret = 0;

    if (inst->impl->IsBinaryImmInst()) {
        ret = (static_cast<ark::compiler::BinaryImmOperation *>(inst->impl))->GetImm();
    } else if (inst->impl->GetOpcode() == ark::compiler::Opcode::Intrinsic) {
        ret = inst->impl->CastToIntrinsic()->GetImm(idx);
    } else {
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return 0;
    }
    return ret;
}

void IsetImmediateStatic(AbckitInst *inst, size_t idx, uint64_t imm)
{
    LIBABCKIT_LOG_FUNC;

    if (IgetImmediateCountStatic(inst) <= idx) {
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return;
    }

    if (inst->impl->IsBinaryImmInst()) {
        if (GetBitLengthUnsigned(imm) <= GetBinaryImmOperationSize(inst->impl->GetOpcode())) {
            (static_cast<ark::compiler::BinaryImmOperation *>(inst->impl))->SetImm(imm);
        } else {
            LIBABCKIT_LOG(DEBUG) << "Immediate type overflow\n";
            SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        }
    } else if (inst->impl->GetOpcode() == ark::compiler::Opcode::Intrinsic) {
        auto instr = inst->impl->CastToIntrinsic();
        if (GetBitLengthUnsigned(imm) <= GetIntrinsicBitImmSize(instr->GetIntrinsicId(), idx)) {
            instr->SetImm(idx, imm);
        } else {
            LIBABCKIT_LOG(DEBUG) << "Immediate type overflow\n";
            SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        }
    } else {
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
    }
}

AbckitBitImmSize IgetImmediateSizeStatic(AbckitInst *inst, size_t idx)
{
    LIBABCKIT_LOG_FUNC;

    if (IgetImmediateCountStatic(inst) <= idx) {
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return AbckitBitImmSize::BITSIZE_0;
    }

    auto immBitSize = AbckitBitImmSize::BITSIZE_0;
    if (inst->impl->IsBinaryImmInst()) {
        immBitSize = GetBinaryImmOperationSize(inst->impl->GetOpcode());
    } else if (inst->impl->GetOpcode() == ark::compiler::Opcode::Intrinsic) {
        auto instr = inst->impl->CastToIntrinsic();
        immBitSize = GetIntrinsicBitImmSize(instr->GetIntrinsicId(), idx);
    } else {
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
    }
    return immBitSize;
}

uint64_t IgetImmediateCountStatic(AbckitInst *inst)
{
    LIBABCKIT_LOG_FUNC;

    uint64_t ret = 0;

    if (inst->impl->IsBinaryImmInst()) {
        ret = 1;
    } else if (inst->impl->GetOpcode() == ark::compiler::Opcode::Intrinsic) {
        auto instr = inst->impl->CastToIntrinsic();
        return instr->HasImms() ? instr->GetImms().size() : 0;
    }
    return ret;
}

AbckitCoreModule *IgetModuleStatic(AbckitInst *inst)
{
    LIBABCKIT_LOG_FUNC;

    if (!inst->impl->IsIntrinsic()) {
        LIBABCKIT_LOG(DEBUG) << "Instruction doesn't have module id\n";
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return nullptr;
    }

    auto intrInst = inst->impl->CastToIntrinsic();
    auto opcode = GetDynamicIntrinsicOpcode(intrInst);
    if (opcode != ABCKIT_ISA_API_DYNAMIC_OPCODE_GETMODULENAMESPACE &&
        opcode != ABCKIT_ISA_API_DYNAMIC_OPCODE_WIDE_GETMODULENAMESPACE) {
        LIBABCKIT_LOG(DEBUG) << "Instruction doesn't have module id\n";
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return nullptr;
    }

    return inst->graph->function->owningModule->md[intrInst->GetImm(0)];
}

uint64_t GetModuleIndex(AbckitGraph *graph, AbckitCoreModule *md)
{
    uint64_t imm = 0;
    for (auto m : graph->function->owningModule->md) {
        if (m == md) {
            break;
        }
        imm++;
    }
    if (imm == graph->function->owningModule->md.size()) {
        LIBABCKIT_LOG(DEBUG) << "Can not find module descriptor for module with name '" << md->moduleName->impl
                             << "'\n";
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return 0;
    }
    return imm;
}

void IsetModuleStatic(AbckitInst *inst, AbckitCoreModule *md)
{
    LIBABCKIT_LOG_FUNC;

    if (!inst->impl->IsIntrinsic()) {
        LIBABCKIT_LOG(DEBUG) << "Instruction doesn't have module id\n";
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return;
    }

    auto intrInst = inst->impl->CastToIntrinsic();
    auto opcode = GetDynamicIntrinsicOpcode(intrInst);
    if (opcode != ABCKIT_ISA_API_DYNAMIC_OPCODE_GETMODULENAMESPACE &&
        opcode != ABCKIT_ISA_API_DYNAMIC_OPCODE_WIDE_GETMODULENAMESPACE) {
        LIBABCKIT_LOG(DEBUG) << "Instruction doesn't have module id\n";
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return;
    }

    uint64_t imm = GetModuleIndex(inst->graph, md);
    if (statuses::GetLastError() != ABCKIT_STATUS_NO_ERROR) {
        return;
    }
    intrInst->SetImm(0, imm);
}

AbckitCoreImportDescriptor *GetImportDescriptorDynamic(AbckitInst *inst, uint64_t idx)
{
    auto *module = inst->graph->function->owningModule;
    for (auto &id : module->id) {
        auto idPayload = GetDynImportDescriptorPayload(id.get());
        if (!idPayload->isRegularImport) {
            continue;
        }
        if (idPayload->moduleRecordIndexOff == idx) {
            return id.get();
        }
    }
    UNREACHABLE();
}

AbckitCoreImportDescriptor *IgetImportDescriptorStatic(AbckitInst *inst)
{
    LIBABCKIT_LOG_FUNC;
    ASSERT(IsDynamic(inst->graph->function->owningModule->target));

    if (!inst->impl->IsIntrinsic()) {
        LIBABCKIT_LOG(DEBUG) << "Instruction doesn't have import descriptor\n";
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return nullptr;
    }

    auto intrInst = inst->impl->CastToIntrinsic();
    auto opcode = GetDynamicIntrinsicOpcode(intrInst);
    if (opcode != ABCKIT_ISA_API_DYNAMIC_OPCODE_LDEXTERNALMODULEVAR &&
        opcode != ABCKIT_ISA_API_DYNAMIC_OPCODE_WIDE_LDEXTERNALMODULEVAR) {
        LIBABCKIT_LOG(DEBUG) << "Instruction doesn't have import descriptor\n";
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return nullptr;
    }

    return GetImportDescriptorDynamic(inst, intrInst->GetImm(0));
}

uint32_t GetImportDescriptorIdxDynamic(AbckitGraph *graph, AbckitCoreImportDescriptor *id)
{
    AbckitCoreModule *m = graph->function->owningModule;
    auto found = std::find_if(m->id.begin(), m->id.end(),
                              [&](std::unique_ptr<AbckitCoreImportDescriptor> const &d) { return d.get() == id; });
    if (found == m->id.end()) {
        LIBABCKIT_LOG(DEBUG) << "Can not find the import in module imports\n";
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return 0;
    }
    return GetDynImportDescriptorPayload(found->get())->moduleRecordIndexOff;
}

void IsetImportDescriptorStatic(AbckitInst *inst, AbckitCoreImportDescriptor *id)
{
    LIBABCKIT_LOG_FUNC;
    ASSERT(IsDynamic(inst->graph->function->owningModule->target));

    if (!inst->impl->IsIntrinsic()) {
        LIBABCKIT_LOG(DEBUG) << "Instruction doesn't have import descriptor\n";
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return;
    }

    auto intrInst = inst->impl->CastToIntrinsic();
    auto opcode = GetDynamicIntrinsicOpcode(intrInst);
    if (opcode != ABCKIT_ISA_API_DYNAMIC_OPCODE_LDEXTERNALMODULEVAR &&
        opcode != ABCKIT_ISA_API_DYNAMIC_OPCODE_WIDE_LDEXTERNALMODULEVAR) {
        LIBABCKIT_LOG(DEBUG) << "Instruction doesn't have import descriptor\n";
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return;
    }

    uint32_t imm = GetImportDescriptorIdxDynamic(inst->graph, id);
    if (statuses::GetLastError() != ABCKIT_STATUS_NO_ERROR) {
        return;
    }
    intrInst->SetImm(0, imm);
}

AbckitCoreExportDescriptor *GetExportDescriptorDynamic(AbckitInst *inst, uint64_t idx)
{
    auto *module = inst->graph->function->owningModule;
    for (auto &ed : module->ed) {
        auto edPayload = GetDynExportDescriptorPayload(ed.get());
        if (edPayload->kind != AbckitDynamicExportKind::ABCKIT_DYNAMIC_EXPORT_KIND_LOCAL_EXPORT) {
            continue;
        }
        if (edPayload->moduleRecordIndexOff == idx) {
            return ed.get();
        }
    }
    UNREACHABLE();
}

AbckitCoreExportDescriptor *IgetExportDescriptorStatic(AbckitInst *inst)
{
    LIBABCKIT_LOG_FUNC;
    ASSERT(IsDynamic(inst->graph->function->owningModule->target));

    if (!inst->impl->IsIntrinsic()) {
        LIBABCKIT_LOG(DEBUG) << "Instruction doesn't have export descriptor\n";
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return nullptr;
    }

    auto intrInst = inst->impl->CastToIntrinsic();
    auto opcode = GetDynamicIntrinsicOpcode(intrInst);
    if (opcode != ABCKIT_ISA_API_DYNAMIC_OPCODE_LDLOCALMODULEVAR &&
        opcode != ABCKIT_ISA_API_DYNAMIC_OPCODE_WIDE_LDLOCALMODULEVAR &&
        opcode != ABCKIT_ISA_API_DYNAMIC_OPCODE_STMODULEVAR &&
        opcode != ABCKIT_ISA_API_DYNAMIC_OPCODE_WIDE_STMODULEVAR) {
        LIBABCKIT_LOG(DEBUG) << "Instruction doesn't have export descriptor\n";
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return nullptr;
    }

    return GetExportDescriptorDynamic(inst, intrInst->GetImm(0));
}

uint32_t GetExportDescriptorIdxDynamic(AbckitGraph *graph, AbckitCoreExportDescriptor *ed)
{
    AbckitCoreModule *m = graph->function->owningModule;
    auto found = std::find_if(m->ed.begin(), m->ed.end(),
                              [&](std::unique_ptr<AbckitCoreExportDescriptor> const &d) { return d.get() == ed; });
    if (found == m->ed.end()) {
        LIBABCKIT_LOG(DEBUG) << "Can not find the import in module imports\n";
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return 0;
    }
    return GetDynExportDescriptorPayload(found->get())->moduleRecordIndexOff;
}

void IsetExportDescriptorStatic(AbckitInst *inst, AbckitCoreExportDescriptor *ed)
{
    LIBABCKIT_LOG_FUNC;
    ASSERT(IsDynamic(inst->graph->function->owningModule->target));

    if (!inst->impl->IsIntrinsic()) {
        LIBABCKIT_LOG(DEBUG) << "Instruction doesn't have export descriptor\n";
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return;
    }

    auto intrInst = inst->impl->CastToIntrinsic();
    auto opcode = GetDynamicIntrinsicOpcode(intrInst);
    if (opcode != ABCKIT_ISA_API_DYNAMIC_OPCODE_LDLOCALMODULEVAR &&
        opcode != ABCKIT_ISA_API_DYNAMIC_OPCODE_WIDE_LDLOCALMODULEVAR &&
        opcode != ABCKIT_ISA_API_DYNAMIC_OPCODE_STMODULEVAR &&
        opcode != ABCKIT_ISA_API_DYNAMIC_OPCODE_WIDE_STMODULEVAR) {
        LIBABCKIT_LOG(DEBUG) << "Instruction doesn't have export descriptor\n";
        SetLastError(ABCKIT_STATUS_BAD_ARGUMENT);
        return;
    }

    uint32_t imm = GetExportDescriptorIdxDynamic(inst->graph, ed);
    if (statuses::GetLastError() != ABCKIT_STATUS_NO_ERROR) {
        return;
    }
    intrInst->SetImm(0, imm);
}

AbckitIsaApiStaticConditionCode IgetConditionCodeStaticStatic(AbckitInst *inst)
{
    return CcToStaticCc(inst->impl->CastToIf()->GetCc());
}

AbckitIsaApiDynamicConditionCode IgetConditionCodeDynamicStatic(AbckitInst *inst)
{
    return CcToDynamicCc(inst->impl->CastToIf()->GetCc());
}

void IsetConditionCodeStaticStatic(AbckitInst *inst, AbckitIsaApiStaticConditionCode cc)
{
    inst->impl->CastToIf()->SetCc(CcToCc(cc));
}

void IsetConditionCodeDynamicStatic(AbckitInst *inst, AbckitIsaApiDynamicConditionCode cc)
{
    inst->impl->CastToIf()->SetCc(CcToCc(cc));
}

}  // namespace libabckit
